

<!DOCTYPE html>
<html lang="zh-CN" data-default-color-scheme=auto>



<head>
  <meta name="referrer" content="no-referrer" />
  <meta charset="UTF-8">
  <link rel="apple-touch-icon" sizes="76x76" href="/img/apple-touch-icon.png">
  <link rel="icon" href="/img/fyy.png">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5.0, shrink-to-fit=no">
  <meta http-equiv="x-ua-compatible" content="ie=edge">
  
  <meta name="theme-color" content="#2f4154">
  <meta name="author" content="fyy">
  <meta name="keywords" content="">
  
    <meta name="description" content="IO 流简介IO 即 Input&#x2F;Output，输入和输出。数据输入到计算机内存的过程即输入，反之输出到外部存储（比如数据库，文件，远程主机）的过程即输出。数据传输过程类似于水流，因此称为流。IO 流在 Java 中分为输入流和输出流，而根据数据的处理方式又分为字节流和字符流。 Java IO 流的 40 多个类都是从如下 4 个抽象类基类中派生出来的。  InputStream&#x2F;Rea">
<meta property="og:type" content="article">
<meta property="og:title" content="IO基础知识">
<meta property="og:url" content="http://example.com/2021/04/06/IO/Java%20IO%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86/index.html">
<meta property="og:site_name" content="fyy-coding">
<meta property="og:description" content="IO 流简介IO 即 Input&#x2F;Output，输入和输出。数据输入到计算机内存的过程即输入，反之输出到外部存储（比如数据库，文件，远程主机）的过程即输出。数据传输过程类似于水流，因此称为流。IO 流在 Java 中分为输入流和输出流，而根据数据的处理方式又分为字节流和字符流。 Java IO 流的 40 多个类都是从如下 4 个抽象类基类中派生出来的。  InputStream&#x2F;Rea">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="c:\Users\69425\AppData\Roaming\Typora\typora-user-images\image-20231016123203978.png">
<meta property="og:image" content="https://oss.javaguide.cn/github/javaguide/java/io/20210609164749122.png">
<meta property="article:published_time" content="2021-04-06T04:13:33.000Z">
<meta property="article:modified_time" content="2024-03-18T15:23:01.002Z">
<meta property="article:author" content="fyy">
<meta property="article:tag" content="IO">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:image" content="c:\Users\69425\AppData\Roaming\Typora\typora-user-images\image-20231016123203978.png">
  
  
  
  <title>IO基础知识 - fyy-coding</title>

  <link  rel="stylesheet" href="https://lib.baomitu.com/twitter-bootstrap/4.6.1/css/bootstrap.min.css" />



  <link  rel="stylesheet" href="https://lib.baomitu.com/github-markdown-css/4.0.0/github-markdown.min.css" />

  <link  rel="stylesheet" href="https://lib.baomitu.com/hint.css/2.7.0/hint.min.css" />

  <link  rel="stylesheet" href="https://lib.baomitu.com/fancybox/3.5.7/jquery.fancybox.min.css" />



<!-- 主题依赖的图标库，不要自行修改 -->
<!-- Do not modify the link that theme dependent icons -->

<link rel="stylesheet" href="//at.alicdn.com/t/font_1749284_hj8rtnfg7um.css">



<link rel="stylesheet" href="//at.alicdn.com/t/font_1736178_lbnruvf0jn.css">


<link  rel="stylesheet" href="/css/main.css" />


  <link id="highlight-css" rel="stylesheet" href="/css/highlight.css" />
  
    <link id="highlight-css-dark" rel="stylesheet" href="/css/highlight-dark.css" />
  




  <script id="fluid-configs">
    var Fluid = window.Fluid || {};
    Fluid.ctx = Object.assign({}, Fluid.ctx)
    var CONFIG = {"hostname":"example.com","root":"/","version":"1.9.7","typing":{"enable":true,"typeSpeed":70,"cursorChar":"_","loop":false,"scope":[]},"anchorjs":{"enable":true,"element":"h1,h2,h3,h4,h5,h6","placement":"left","visible":"hover","icon":""},"progressbar":{"enable":true,"height_px":3,"color":"#29d","options":{"showSpinner":false,"trickleSpeed":100}},"code_language":{"enable":true,"default":"TEXT"},"copy_btn":true,"image_caption":{"enable":true},"image_zoom":{"enable":true,"img_url_replace":["",""]},"toc":{"enable":true,"placement":"right","headingSelector":"h1,h2,h3,h4,h5,h6","collapseDepth":0},"lazyload":{"enable":true,"loading_img":"/img/loading.gif","onlypost":false,"offset_factor":2},"web_analytics":{"enable":true,"follow_dnt":true,"baidu":null,"google":{"measurement_id":null},"tencent":{"sid":null,"cid":null},"woyaola":null,"cnzz":null,"leancloud":{"app_id":"ufph8TbmK43d1JfQxLvss4KY-MdYXbMMI","app_key":"96APiPOtZEiCQlv5h3jjZfbC","server_url":null,"path":"window.location.pathname","ignore_local":false}},"search_path":"/local-search.xml","include_content_in_search":true};

    if (CONFIG.web_analytics.follow_dnt) {
      var dntVal = navigator.doNotTrack || window.doNotTrack || navigator.msDoNotTrack;
      Fluid.ctx.dnt = dntVal && (dntVal.startsWith('1') || dntVal.startsWith('yes') || dntVal.startsWith('on'));
    }
  </script>
  <script  src="/js/utils.js" ></script>
  <script  src="/js/color-schema.js" ></script>
  

  

  
    <!-- Google tag (gtag.js) -->
    <script async>
      if (!Fluid.ctx.dnt) {
        Fluid.utils.createScript("https://www.googletagmanager.com/gtag/js?id=", function() {
          window.dataLayer = window.dataLayer || [];
          function gtag() {
            dataLayer.push(arguments);
          }
          gtag('js', new Date());
          gtag('config', '');
        });
      }
    </script>
  

  

  

  

  
    
  



  
<meta name="generator" content="Hexo 6.3.0"></head>


<body>
  

  <header>
    

<div class="header-inner" style="height: 70vh;">
  <nav id="navbar" class="navbar fixed-top  navbar-expand-lg navbar-dark scrolling-navbar">
  <div class="container">
    <a class="navbar-brand" href="/">
      <strong>fyy-coding</strong>
    </a>

    <button id="navbar-toggler-btn" class="navbar-toggler" type="button" data-toggle="collapse"
            data-target="#navbarSupportedContent"
            aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
      <div class="animated-icon"><span></span><span></span><span></span></div>
    </button>

    <!-- Collapsible content -->
    <div class="collapse navbar-collapse" id="navbarSupportedContent">
      <ul class="navbar-nav ml-auto text-center">
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/" target="_self">
                <i class="iconfont icon-home-fill"></i>
                <span>首页</span>
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/archives/" target="_self">
                <i class="iconfont icon-archive-fill"></i>
                <span>归档</span>
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/categories/" target="_self">
                <i class="iconfont icon-category-fill"></i>
                <span>分类</span>
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/tags/" target="_self">
                <i class="iconfont icon-tags-fill"></i>
                <span>标签</span>
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/about/" target="_self">
                <i class="iconfont icon-user-fill"></i>
                <span>关于</span>
              </a>
            </li>
          
        
        
          <li class="nav-item" id="search-btn">
            <a class="nav-link" target="_self" href="javascript:;" data-toggle="modal" data-target="#modalSearch" aria-label="Search">
              <i class="iconfont icon-search"></i>
            </a>
          </li>
          
        
        
          <li class="nav-item" id="color-toggle-btn">
            <a class="nav-link" target="_self" href="javascript:;" aria-label="Color Toggle">
              <i class="iconfont icon-dark" id="color-toggle-icon"></i>
            </a>
          </li>
        
      </ul>
    </div>
  </div>
</nav>

  

<div id="banner" class="banner" parallax=true
     style="background: url('/img/default.png') no-repeat center center; background-size: cover;">
  <div class="full-bg-img">
    <div class="mask flex-center" style="background-color: rgba(0, 0, 0, 0.3)">
      <div class="banner-text text-center fade-in-up">
        <div class="h2">
          
            <span id="subtitle" data-typed-text="IO基础知识"></span>
          
        </div>

        
          
  <div class="mt-3">
    
    
      <span class="post-meta">
        <i class="iconfont icon-date-fill" aria-hidden="true"></i>
        <time datetime="2021-04-06 12:13" pubdate>
          2021年4月6日 中午
        </time>
      </span>
    
  </div>

  <div class="mt-1">
    
      <span class="post-meta mr-2">
        <i class="iconfont icon-chart"></i>
        
          4.5k 字
        
      </span>
    

    
      <span class="post-meta mr-2">
        <i class="iconfont icon-clock-fill"></i>
        
        
        
          38 分钟
        
      </span>
    

    
    
      
        <span id="leancloud-page-views-container" class="post-meta" style="display: none">
          <i class="iconfont icon-eye" aria-hidden="true"></i>
          <span id="leancloud-page-views"></span> 次
        </span>
        
      
    
  </div>


        
      </div>

      
    </div>
  </div>
</div>

</div>

  </header>

  <main>
    
      

<div class="container-fluid nopadding-x">
  <div class="row nomargin-x">
    <div class="side-col d-none d-lg-block col-lg-2">
      

    </div>

    <div class="col-lg-8 nopadding-x-md">
      <div class="container nopadding-x-md" id="board-ctn">
        <div id="board">
          <article class="post-content mx-auto">
            <h1 id="seo-header">IO基础知识</h1>
            
            
              <div class="markdown-body">
                
                <h2 id="IO-流简介"><a href="#IO-流简介" class="headerlink" title="IO 流简介"></a>IO 流简介</h2><p>IO 即 <code>Input/Output</code>，输入和输出。数据输入到计算机内存的过程即输入，反之输出到外部存储（比如数据库，文件，远程主机）的过程即输出。数据传输过程类似于水流，因此称为流。IO 流在 Java 中分为输入流和输出流，而根据数据的处理方式又分为字节流和字符流。</p>
<p>Java IO 流的 40 多个类都是从如下 4 个抽象类基类中派生出来的。</p>
<ul>
<li><code>InputStream</code>&#x2F;<code>Reader</code>: 所有的输入流的基类，前者是字节输入流，后者是字符输入流。</li>
<li><code>OutputStream</code>&#x2F;<code>Writer</code>: 所有输出流的基类，前者是字节输出流，后者是字符输出流。</li>
</ul>
<h2 id="字节流"><a href="#字节流" class="headerlink" title="字节流"></a>字节流</h2><h3 id="InputStream（字节输入流）"><a href="#InputStream（字节输入流）" class="headerlink" title="InputStream（字节输入流）"></a>InputStream（字节输入流）</h3><p><code>InputStream</code>用于从源头（通常是文件）读取数据（字节信息）到内存中，<code>java.io.InputStream</code>抽象类是所有字节输入流的父类。</p>
<p><code>InputStream</code> 常用方法：</p>
<ul>
<li><code>read()</code>：返回输入流中下一个字节的数据。返回的值介于 0 到 255 之间。如果未读取任何字节，则代码返回 <code>-1</code> ，表示文件结束。</li>
<li><code>read(byte b[ ])</code> : 从输入流中读取一些字节存储到数组 <code>b</code> 中。如果数组 <code>b</code> 的长度为零，则不读取。如果没有可用字节读取，返回 <code>-1</code>。如果有可用字节读取，则最多读取的字节数最多等于 <code>b.length</code> ， 返回读取的字节数。这个方法等价于 <code>read(b, 0, b.length)</code>。</li>
<li><code>read(byte b[], int off, int len)</code>：在<code>read(byte b[ ])</code> 方法的基础上增加了 <code>off</code> 参数（偏移量）和 <code>len</code> 参数（要读取的最大字节数）。</li>
<li><code>skip(long n)</code>：忽略输入流中的 n 个字节 ,返回实际忽略的字节数。</li>
<li><code>available()</code>：返回输入流中可以读取的字节数。</li>
<li><code>close()</code>：关闭输入流释放相关的系统资源。</li>
</ul>
<p>从 Java 9 开始，<code>InputStream</code> 新增加了多个实用的方法：</p>
<ul>
<li><code>readAllBytes()</code>：读取输入流中的所有字节，返回字节数组。</li>
<li><code>readNBytes(byte[] b, int off, int len)</code>：阻塞直到读取 <code>len</code> 个字节。</li>
<li><code>transferTo(OutputStream out)</code>：将所有字节从一个输入流传递到一个输出流。</li>
</ul>
<p><code>FileInputStream</code> 是一个比较常用的字节输入流对象，可直接指定文件路径，可以直接读取单字节数据，也可以读取至字节数组中。</p>
<p><code>FileInputStream</code> 代码示例：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">try</span> (<span class="hljs-type">InputStream</span> <span class="hljs-variable">fis</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">FileInputStream</span>(<span class="hljs-string">&quot;input.txt&quot;</span>)) &#123;<br>    System.out.println(<span class="hljs-string">&quot;Number of remaining bytes:&quot;</span><br>            + fis.available());<br>    <span class="hljs-type">int</span> content;<br>    <span class="hljs-type">long</span> <span class="hljs-variable">skip</span> <span class="hljs-operator">=</span> fis.skip(<span class="hljs-number">2</span>);<br>    System.out.println(<span class="hljs-string">&quot;The actual number of bytes skipped:&quot;</span> + skip);<br>    System.out.print(<span class="hljs-string">&quot;The content read from file:&quot;</span>);<br>    <span class="hljs-keyword">while</span> ((content = fis.read()) != -<span class="hljs-number">1</span>) &#123;<br>        System.out.print((<span class="hljs-type">char</span>) content);<br>    &#125;<br>&#125; <span class="hljs-keyword">catch</span> (IOException e) &#123;<br>    e.printStackTrace();<br>&#125;<br></code></pre></td></tr></table></figure>



<p>不过，一般我们是不会直接单独使用 <code>FileInputStream</code> ，通常会配合 <code>BufferedInputStream</code>（字节缓冲输入流）来使用。</p>
<p>像下面这段代码在我们的项目中就比较常见，我们通过 <code>readAllBytes()</code> 读取输入流所有字节并将其直接赋值给一个 <code>String</code> 对象。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">// 新建一个 BufferedInputStream 对象</span><br><span class="hljs-type">BufferedInputStream</span> <span class="hljs-variable">bufferedInputStream</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">BufferedInputStream</span>(<span class="hljs-keyword">new</span> <span class="hljs-title class_">FileInputStream</span>(<span class="hljs-string">&quot;input.txt&quot;</span>));<br><span class="hljs-comment">// 读取文件的内容并复制到 String 对象中</span><br><span class="hljs-type">String</span> <span class="hljs-variable">result</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">String</span>(bufferedInputStream.readAllBytes());<br>System.out.println(result);<br></code></pre></td></tr></table></figure>



<p><code>DataInputStream</code> 用于读取指定类型数据，不能单独使用，必须结合其它流，比如 <code>FileInputStream</code>。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-type">FileInputStream</span> <span class="hljs-variable">fileInputStream</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">FileInputStream</span>(<span class="hljs-string">&quot;input.txt&quot;</span>);<br><span class="hljs-comment">//必须将fileInputStream作为构造参数才能使用</span><br><span class="hljs-type">DataInputStream</span> <span class="hljs-variable">dataInputStream</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">DataInputStream</span>(fileInputStream);<br><span class="hljs-comment">//可以读取任意具体的类型数据</span><br>dataInputStream.readBoolean();<br>dataInputStream.readInt();<br>dataInputStream.readUTF();<br></code></pre></td></tr></table></figure>



<p><code>ObjectInputStream</code> 用于从输入流中读取 Java 对象（反序列化），<code>ObjectOutputStream</code> 用于将对象写入到输出流(序列化)。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-type">ObjectInputStream</span> <span class="hljs-variable">input</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">ObjectInputStream</span>(<span class="hljs-keyword">new</span> <span class="hljs-title class_">FileInputStream</span>(<span class="hljs-string">&quot;object.data&quot;</span>));<br><span class="hljs-type">MyClass</span> <span class="hljs-variable">object</span> <span class="hljs-operator">=</span> (MyClass) input.readObject();<br>input.close();<br></code></pre></td></tr></table></figure>

<p>另外，用于序列化和反序列化的类必须实现 <code>Serializable</code> 接口，对象中如果有属性不想被序列化，使用 <code>transient</code> 修饰。</p>
<h3 id="OutputStream（字节输出流）"><a href="#OutputStream（字节输出流）" class="headerlink" title="OutputStream（字节输出流）"></a>OutputStream（字节输出流）</h3><p><code>OutputStream</code>用于将数据（字节信息）写入到目的地（通常是文件），<code>java.io.OutputStream</code>抽象类是所有字节输出流的父类。</p>
<p><code>OutputStream</code> 常用方法：</p>
<ul>
<li><code>write(int b)</code>：将特定字节写入输出流。</li>
<li><code>write(byte b[ ])</code> : 将数组<code>b</code> 写入到输出流，等价于 <code>write(b, 0, b.length)</code> 。</li>
<li><code>write(byte[] b, int off, int len)</code> : 在<code>write(byte b[ ])</code> 方法的基础上增加了 <code>off</code> 参数（偏移量）和 <code>len</code> 参数（要读取的最大字节数）。</li>
<li><code>flush()</code>：刷新此输出流并强制写出所有缓冲的输出字节。</li>
<li><code>close()</code>：关闭输出流释放相关的系统资源。</li>
</ul>
<p><code>FileOutputStream</code> 是最常用的字节输出流对象，可直接指定文件路径，可以直接输出单字节数据，也可以输出指定的字节数组。</p>
<p><code>FileOutputStream</code> 代码示例：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">try</span> (<span class="hljs-type">FileOutputStream</span> <span class="hljs-variable">output</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">FileOutputStream</span>(<span class="hljs-string">&quot;output.txt&quot;</span>)) &#123;<br>    <span class="hljs-type">byte</span>[] array = <span class="hljs-string">&quot;test&quot;</span>.getBytes();<br>    output.write(array);<br>&#125; <span class="hljs-keyword">catch</span> (IOException e) &#123;<br>    e.printStackTrace(<br>    );<br>&#125;<br></code></pre></td></tr></table></figure>



<p>类似于 <code>FileInputStream</code>，<code>FileOutputStream</code> 通常也会配合 <code>BufferedOutputStream</code>（字节缓冲输出流，后文会讲到）来使用。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-type">FileOutputStream</span> <span class="hljs-variable">fileOutputStream</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">FileOutputStream</span>(<span class="hljs-string">&quot;output.txt&quot;</span>);<br><span class="hljs-type">BufferedOutputStream</span> <span class="hljs-variable">bos</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">BufferedOutputStream</span>(fileOutputStream)<br></code></pre></td></tr></table></figure>



<p><strong><code>DataOutputStream</code></strong> 用于写入指定类型数据，不能单独使用，必须结合其它流，比如 <code>FileOutputStream</code> 。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">// 输出流</span><br><span class="hljs-type">FileOutputStream</span> <span class="hljs-variable">fileOutputStream</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">FileOutputStream</span>(<span class="hljs-string">&quot;out.txt&quot;</span>);<br><span class="hljs-type">DataOutputStream</span> <span class="hljs-variable">dataOutputStream</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">DataOutputStream</span>(fileOutputStream);<br><span class="hljs-comment">// 输出任意数据类型</span><br>dataOutputStream.writeBoolean(<span class="hljs-literal">true</span>);<br>dataOutputStream.writeByte(<span class="hljs-number">1</span>);<br></code></pre></td></tr></table></figure>



<p><code>ObjectInputStream</code> 用于从输入流中读取 Java 对象（<code>ObjectInputStream</code>,反序列化），<code>ObjectOutputStream</code>将对象写入到输出流(<code>ObjectOutputStream</code>，序列化)。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-type">ObjectOutputStream</span> <span class="hljs-variable">output</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">ObjectOutputStream</span>(<span class="hljs-keyword">new</span> <span class="hljs-title class_">FileOutputStream</span>(<span class="hljs-string">&quot;file.txt&quot;</span>)<br><span class="hljs-type">Person</span> <span class="hljs-variable">person</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Person</span>(<span class="hljs-string">&quot;Guide哥&quot;</span>, <span class="hljs-string">&quot;JavaGuide作者&quot;</span>);<br>output.writeObject(person);<br></code></pre></td></tr></table></figure>



<h2 id="字符流"><a href="#字符流" class="headerlink" title="字符流"></a>字符流</h2><p>不管是文件读写还是网络发送接收，信息的最小存储单元都是字节。 <strong>那为什么 I&#x2F;O 流操作要分为字节流操作和字符流操作呢？</strong></p>
<p>个人认为主要有两点原因：</p>
<ul>
<li>字符流是由 Java 虚拟机将字节转换得到的，这个过程还算是比较耗时。</li>
<li>如果我们不知道编码类型就很容易出现乱码问题。</li>
</ul>
<p>乱码问题这个很容易就可以复现，我们只需要将上面提到的 <code>FileInputStream</code> 代码示例中的 <code>input.txt</code> 文件内容改为中文即可，原代码不需要改动。可以很明显地看到读取出来的内容已经变成了乱码。</p>
<p>因此，I&#x2F;O 流就干脆提供了一个直接操作字符的接口，方便我们平时对字符进行流操作。如果音频文件、图片等媒体文件用字节流比较好，如果涉及到字符的话使用字符流比较好。</p>
<p>字符流默认采用的是 <code>Unicode</code> 编码，我们可以通过构造方法自定义编码。</p>
<p>常用字符编码所占字节数？</p>
<ul>
<li><code>utf8</code> :英文占 1 字节，中文占 3 字节</li>
<li><code>unicode</code>：任何字符都占 2 个字节</li>
<li><code>gbk</code>：英文占 1 字节，中文占 2 字节</li>
</ul>
<h3 id="Reader（字符输入流）"><a href="#Reader（字符输入流）" class="headerlink" title="Reader（字符输入流）"></a>Reader（字符输入流）</h3><p><code>Reader</code>用于从源头（通常是文件）读取数据（字符信息）到内存中，<code>java.io.Reader</code>抽象类是所有字符输入流的父类。</p>
<p><code>Reader</code> 用于读取文本， <code>InputStream</code> 用于读取原始字节。</p>
<p><code>Reader</code> 常用方法：</p>
<ul>
<li><code>read()</code> : 从输入流读取一个字符。</li>
<li><code>read(char[] cbuf)</code> : 从输入流中读取一些字符，并将它们存储到字符数组 <code>cbuf</code>中，等价于 <code>read(cbuf, 0, cbuf.length)</code> 。</li>
<li><code>read(char[] cbuf, int off, int len)</code>：在<code>read(char[] cbuf)</code> 方法的基础上增加了 <code>off</code> 参数（偏移量）和 <code>len</code> 参数（要读取的最大字符数）。</li>
<li><code>skip(long n)</code>：忽略输入流中的 n 个字符 ,返回实际忽略的字符数。</li>
<li><code>close()</code> : 关闭输入流并释放相关的系统资源。</li>
</ul>
<p><code>InputStreamReader</code> 是字节流转换为字符流的桥梁，其子类 <code>FileReader</code> 是基于该基础上的封装，可以直接操作字符文件。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">// 字节流转换为字符流的桥梁</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">InputStreamReader</span> <span class="hljs-keyword">extends</span> <span class="hljs-title class_">Reader</span> &#123;<br>&#125;<br><span class="hljs-comment">// 用于读取字符文件</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">FileReader</span> <span class="hljs-keyword">extends</span> <span class="hljs-title class_">InputStreamReader</span> &#123;<br>&#125;<br></code></pre></td></tr></table></figure>



<p><code>FileReader</code> 代码示例：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">try</span> (<span class="hljs-type">FileReader</span> <span class="hljs-variable">fileReader</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">FileReader</span>(<span class="hljs-string">&quot;input.txt&quot;</span>);) &#123;<br>    <span class="hljs-type">int</span> content;<br>    <span class="hljs-type">long</span> <span class="hljs-variable">skip</span> <span class="hljs-operator">=</span> fileReader.skip(<span class="hljs-number">3</span>);<br>    System.out.println(<span class="hljs-string">&quot;The actual number of bytes skipped:&quot;</span> + skip);<br>    System.out.print(<span class="hljs-string">&quot;The content read from file:&quot;</span>);<br>    <span class="hljs-keyword">while</span> ((content = fileReader.read()) != -<span class="hljs-number">1</span>) &#123;<br>        System.out.print((<span class="hljs-type">char</span>) content);<br>    &#125;<br>&#125; <span class="hljs-keyword">catch</span> (IOException e) &#123;<br>    e.printStackTrace();<br>&#125;<br></code></pre></td></tr></table></figure>



<h3 id="Writer（字符输出流）"><a href="#Writer（字符输出流）" class="headerlink" title="Writer（字符输出流）"></a>Writer（字符输出流）</h3><p><code>Writer</code>用于将数据（字符信息）写入到目的地（通常是文件），<code>java.io.Writer</code>抽象类是所有字符输出流的父类。</p>
<p><code>Writer</code> 常用方法：</p>
<ul>
<li><code>write(int c)</code> : 写入单个字符。</li>
<li><code>write(char[] cbuf)</code>：写入字符数组 <code>cbuf</code>，等价于<code>write(cbuf, 0, cbuf.length)</code>。</li>
<li><code>write(char[] cbuf, int off, int len)</code>：在<code>write(char[] cbuf)</code> 方法的基础上增加了 <code>off</code> 参数（偏移量）和 <code>len</code> 参数（要读取的最大字符数）。</li>
<li><code>write(String str)</code>：写入字符串，等价于 <code>write(str, 0, str.length())</code> 。</li>
<li><code>write(String str, int off, int len)</code>：在<code>write(String str)</code> 方法的基础上增加了 <code>off</code> 参数（偏移量）和 <code>len</code> 参数（要读取的最大字符数）。</li>
<li><code>append(CharSequence csq)</code>：将指定的字符序列附加到指定的 <code>Writer</code> 对象并返回该 <code>Writer</code> 对象。</li>
<li><code>append(char c)</code>：将指定的字符附加到指定的 <code>Writer</code> 对象并返回该 <code>Writer</code> 对象。</li>
<li><code>flush()</code>：刷新此输出流并强制写出所有缓冲的输出字符。</li>
<li><code>close()</code>:关闭输出流释放相关的系统资源。</li>
</ul>
<p><code>OutputStreamWriter</code> 是字符流转换为字节流的桥梁，其子类 <code>FileWriter</code> 是基于该基础上的封装，可以直接将字符写入到文件。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">// 字符流转换为字节流的桥梁</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">OutputStreamWriter</span> <span class="hljs-keyword">extends</span> <span class="hljs-title class_">Writer</span> &#123;<br>&#125;<br><span class="hljs-comment">// 用于写入字符到文件</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">FileWriter</span> <span class="hljs-keyword">extends</span> <span class="hljs-title class_">OutputStreamWriter</span> &#123;<br>&#125;<br></code></pre></td></tr></table></figure>



<p><code>FileWriter</code> 代码示例：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">try</span> (<span class="hljs-type">Writer</span> <span class="hljs-variable">output</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">FileWriter</span>(<span class="hljs-string">&quot;output.txt&quot;</span>)) &#123;<br>    output.write(<span class="hljs-string">&quot;你好，我是Guide。&quot;</span>);<br>&#125; <span class="hljs-keyword">catch</span> (IOException e) &#123;<br>    e.printStackTrace();<br>&#125;<br></code></pre></td></tr></table></figure>



<h2 id="字节缓冲流"><a href="#字节缓冲流" class="headerlink" title="字节缓冲流"></a>字节缓冲流</h2><p>IO 操作是很消耗性能的，缓冲流将数据加载至缓冲区，一次性读取&#x2F;写入多个字节，从而避免频繁的 IO 操作，提高流的传输效率。</p>
<p>字节缓冲流这里采用了装饰器模式来增强 <code>InputStream</code> 和<code>OutputStream</code>子类对象的功能。</p>
<p>举个例子，我们可以通过 <code>BufferedInputStream</code>（字节缓冲输入流）来增强 <code>FileInputStream</code> 的功能。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">// 新建一个 BufferedInputStream 对象</span><br><span class="hljs-type">BufferedInputStream</span> <span class="hljs-variable">bufferedInputStream</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">BufferedInputStream</span>(<span class="hljs-keyword">new</span> <span class="hljs-title class_">FileInputStream</span>(<span class="hljs-string">&quot;input.txt&quot;</span>));<br></code></pre></td></tr></table></figure>



<p>字节流和字节缓冲流的性能差别主要体现在我们使用两者的时候都是调用 <code>write(int b)</code> 和 <code>read()</code> 这两个一次只读取一个字节的方法的时候。由于字节缓冲流内部有缓冲区（字节数组），因此，字节缓冲流会先将读取到的字节存放在缓存区，大幅减少 IO 次数，提高读取效率。</p>
<p>我使用 <code>write(int b)</code> 和 <code>read()</code> 方法，分别通过字节流和字节缓冲流复制一个 <code>524.9 mb</code> 的 PDF 文件耗时对比如下：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs plain">使用缓冲流复制PDF文件总耗时:15428 毫秒<br>使用普通字节流复制PDF文件总耗时:2555062 毫秒<br></code></pre></td></tr></table></figure>

<p>两者耗时差别非常大，缓冲流耗费的时间是字节流的 1&#x2F;165。</p>
<p>测试代码如下:</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Test</span><br><span class="hljs-keyword">void</span> <span class="hljs-title function_">copy_pdf_to_another_pdf_buffer_stream</span><span class="hljs-params">()</span> &#123;<br>    <span class="hljs-comment">// 记录开始时间</span><br>    <span class="hljs-type">long</span> <span class="hljs-variable">start</span> <span class="hljs-operator">=</span> System.currentTimeMillis();<br>    <span class="hljs-keyword">try</span> (<span class="hljs-type">BufferedInputStream</span> <span class="hljs-variable">bis</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">BufferedInputStream</span>(<span class="hljs-keyword">new</span> <span class="hljs-title class_">FileInputStream</span>(<span class="hljs-string">&quot;深入理解计算机操作系统.pdf&quot;</span>));<br>         <span class="hljs-type">BufferedOutputStream</span> <span class="hljs-variable">bos</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">BufferedOutputStream</span>(<span class="hljs-keyword">new</span> <span class="hljs-title class_">FileOutputStream</span>(<span class="hljs-string">&quot;深入理解计算机操作系统-副本.pdf&quot;</span>))) &#123;<br>        <span class="hljs-type">int</span> content;<br>        <span class="hljs-keyword">while</span> ((content = bis.read()) != -<span class="hljs-number">1</span>) &#123;<br>            bos.write(content);<br>        &#125;<br>    &#125; <span class="hljs-keyword">catch</span> (IOException e) &#123;<br>        e.printStackTrace();<br>    &#125;<br>    <span class="hljs-comment">// 记录结束时间</span><br>    <span class="hljs-type">long</span> <span class="hljs-variable">end</span> <span class="hljs-operator">=</span> System.currentTimeMillis();<br>    System.out.println(<span class="hljs-string">&quot;使用缓冲流复制PDF文件总耗时:&quot;</span> + (end - start) + <span class="hljs-string">&quot; 毫秒&quot;</span>);<br>&#125;<br><br><span class="hljs-meta">@Test</span><br><span class="hljs-keyword">void</span> <span class="hljs-title function_">copy_pdf_to_another_pdf_stream</span><span class="hljs-params">()</span> &#123;<br>    <span class="hljs-comment">// 记录开始时间</span><br>    <span class="hljs-type">long</span> <span class="hljs-variable">start</span> <span class="hljs-operator">=</span> System.currentTimeMillis();<br>    <span class="hljs-keyword">try</span> (<span class="hljs-type">FileInputStream</span> <span class="hljs-variable">fis</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">FileInputStream</span>(<span class="hljs-string">&quot;深入理解计算机操作系统.pdf&quot;</span>);<br>         <span class="hljs-type">FileOutputStream</span> <span class="hljs-variable">fos</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">FileOutputStream</span>(<span class="hljs-string">&quot;深入理解计算机操作系统-副本.pdf&quot;</span>)) &#123;<br>        <span class="hljs-type">int</span> content;<br>        <span class="hljs-keyword">while</span> ((content = fis.read()) != -<span class="hljs-number">1</span>) &#123;<br>            fos.write(content);<br>        &#125;<br>    &#125; <span class="hljs-keyword">catch</span> (IOException e) &#123;<br>        e.printStackTrace();<br>    &#125;<br>    <span class="hljs-comment">// 记录结束时间</span><br>    <span class="hljs-type">long</span> <span class="hljs-variable">end</span> <span class="hljs-operator">=</span> System.currentTimeMillis();<br>    System.out.println(<span class="hljs-string">&quot;使用普通流复制PDF文件总耗时:&quot;</span> + (end - start) + <span class="hljs-string">&quot; 毫秒&quot;</span>);<br>&#125;<br></code></pre></td></tr></table></figure>



<p>如果是调用 <code>read(byte b[])</code> 和 <code>write(byte b[], int off, int len)</code> 这两个写入一个字节数组的方法的话，只要字节数组的大小合适，两者的性能差距其实不大，基本可以忽略。</p>
<p>这次我们使用 <code>read(byte b[])</code> 和 <code>write(byte b[], int off, int len)</code> 方法，分别通过字节流和字节缓冲流复制一个 524.9 mb 的 PDF 文件耗时对比如下：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs plain">使用缓冲流复制PDF文件总耗时:695 毫秒<br>使用普通字节流复制PDF文件总耗时:989 毫秒<br></code></pre></td></tr></table></figure>

<p>两者耗时差别不是很大，缓冲流的性能要略微好一点点。</p>
<p>测试代码如下：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Test</span><br><span class="hljs-keyword">void</span> <span class="hljs-title function_">copy_pdf_to_another_pdf_with_byte_array_buffer_stream</span><span class="hljs-params">()</span> &#123;<br>    <span class="hljs-comment">// 记录开始时间</span><br>    <span class="hljs-type">long</span> <span class="hljs-variable">start</span> <span class="hljs-operator">=</span> System.currentTimeMillis();<br>    <span class="hljs-keyword">try</span> (<span class="hljs-type">BufferedInputStream</span> <span class="hljs-variable">bis</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">BufferedInputStream</span>(<span class="hljs-keyword">new</span> <span class="hljs-title class_">FileInputStream</span>(<span class="hljs-string">&quot;深入理解计算机操作系统.pdf&quot;</span>));<br>         <span class="hljs-type">BufferedOutputStream</span> <span class="hljs-variable">bos</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">BufferedOutputStream</span>(<span class="hljs-keyword">new</span> <span class="hljs-title class_">FileOutputStream</span>(<span class="hljs-string">&quot;深入理解计算机操作系统-副本.pdf&quot;</span>))) &#123;<br>        <span class="hljs-type">int</span> len;<br>        <span class="hljs-type">byte</span>[] bytes = <span class="hljs-keyword">new</span> <span class="hljs-title class_">byte</span>[<span class="hljs-number">4</span> * <span class="hljs-number">1024</span>];<br>        <span class="hljs-keyword">while</span> ((len = bis.read(bytes)) != -<span class="hljs-number">1</span>) &#123;<br>            bos.write(bytes, <span class="hljs-number">0</span>, len);<br>        &#125;<br>    &#125; <span class="hljs-keyword">catch</span> (IOException e) &#123;<br>        e.printStackTrace();<br>    &#125;<br>    <span class="hljs-comment">// 记录结束时间</span><br>    <span class="hljs-type">long</span> <span class="hljs-variable">end</span> <span class="hljs-operator">=</span> System.currentTimeMillis();<br>    System.out.println(<span class="hljs-string">&quot;使用缓冲流复制PDF文件总耗时:&quot;</span> + (end - start) + <span class="hljs-string">&quot; 毫秒&quot;</span>);<br>&#125;<br><br><span class="hljs-meta">@Test</span><br><span class="hljs-keyword">void</span> <span class="hljs-title function_">copy_pdf_to_another_pdf_with_byte_array_stream</span><span class="hljs-params">()</span> &#123;<br>    <span class="hljs-comment">// 记录开始时间</span><br>    <span class="hljs-type">long</span> <span class="hljs-variable">start</span> <span class="hljs-operator">=</span> System.currentTimeMillis();<br>    <span class="hljs-keyword">try</span> (<span class="hljs-type">FileInputStream</span> <span class="hljs-variable">fis</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">FileInputStream</span>(<span class="hljs-string">&quot;深入理解计算机操作系统.pdf&quot;</span>);<br>         <span class="hljs-type">FileOutputStream</span> <span class="hljs-variable">fos</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">FileOutputStream</span>(<span class="hljs-string">&quot;深入理解计算机操作系统-副本.pdf&quot;</span>)) &#123;<br>        <span class="hljs-type">int</span> len;<br>        <span class="hljs-type">byte</span>[] bytes = <span class="hljs-keyword">new</span> <span class="hljs-title class_">byte</span>[<span class="hljs-number">4</span> * <span class="hljs-number">1024</span>];<br>        <span class="hljs-keyword">while</span> ((len = fis.read(bytes)) != -<span class="hljs-number">1</span>) &#123;<br>            fos.write(bytes, <span class="hljs-number">0</span>, len);<br>        &#125;<br>    &#125; <span class="hljs-keyword">catch</span> (IOException e) &#123;<br>        e.printStackTrace();<br>    &#125;<br>    <span class="hljs-comment">// 记录结束时间</span><br>    <span class="hljs-type">long</span> <span class="hljs-variable">end</span> <span class="hljs-operator">=</span> System.currentTimeMillis();<br>    System.out.println(<span class="hljs-string">&quot;使用普通流复制PDF文件总耗时:&quot;</span> + (end - start) + <span class="hljs-string">&quot; 毫秒&quot;</span>);<br>&#125;<br></code></pre></td></tr></table></figure>



<h3 id="BufferedInputStream（字节缓冲输入流）"><a href="#BufferedInputStream（字节缓冲输入流）" class="headerlink" title="BufferedInputStream（字节缓冲输入流）"></a>BufferedInputStream（字节缓冲输入流）</h3><p><code>BufferedInputStream</code> 从源头（通常是文件）读取数据（字节信息）到内存的过程中不会一个字节一个字节的读取，而是会先将读取到的字节存放在缓存区，并从内部缓冲区中单独读取字节。这样大幅减少了 IO 次数，提高了读取效率。</p>
<p><code>BufferedInputStream</code> 内部维护了一个缓冲区，这个缓冲区实际就是一个字节数组，通过阅读 <code>BufferedInputStream</code> 源码即可得到这个结论。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">BufferedInputStream</span> <span class="hljs-keyword">extends</span> <span class="hljs-title class_">FilterInputStream</span> &#123;<br>    <span class="hljs-comment">// 内部缓冲区数组</span><br>    <span class="hljs-keyword">protected</span> <span class="hljs-keyword">volatile</span> <span class="hljs-type">byte</span> buf[];<br>    <span class="hljs-comment">// 缓冲区的默认大小</span><br>    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-type">int</span> <span class="hljs-variable">DEFAULT_BUFFER_SIZE</span> <span class="hljs-operator">=</span> <span class="hljs-number">8192</span>;<br>    <span class="hljs-comment">// 使用默认的缓冲区大小</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-title function_">BufferedInputStream</span><span class="hljs-params">(InputStream in)</span> &#123;<br>        <span class="hljs-built_in">this</span>(in, DEFAULT_BUFFER_SIZE);<br>    &#125;<br>    <span class="hljs-comment">// 自定义缓冲区大小</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-title function_">BufferedInputStream</span><span class="hljs-params">(InputStream in, <span class="hljs-type">int</span> size)</span> &#123;<br>        <span class="hljs-built_in">super</span>(in);<br>        <span class="hljs-keyword">if</span> (size &lt;= <span class="hljs-number">0</span>) &#123;<br>            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">IllegalArgumentException</span>(<span class="hljs-string">&quot;Buffer size &lt;= 0&quot;</span>);<br>        &#125;<br>        buf = <span class="hljs-keyword">new</span> <span class="hljs-title class_">byte</span>[size];<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<p>缓冲区的大小默认为 <strong>8192</strong> 字节，可以通过 <code>BufferedInputStream(InputStream in, int size)</code> 这个构造方法来指定缓冲区的大小。</p>
<h3 id="BufferedOutputStream（字节缓冲输出流）"><a href="#BufferedOutputStream（字节缓冲输出流）" class="headerlink" title="BufferedOutputStream（字节缓冲输出流）"></a>BufferedOutputStream（字节缓冲输出流）</h3><p><code>BufferedOutputStream</code> 将数据（字节信息）写入到目的地（通常是文件）的过程中不会一个字节一个字节的写入，而是会先将要写入的字节存放在缓存区，并从内部缓冲区中单独写入字节。这样大幅减少了 IO 次数，提高了读取效率</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">try</span> (<span class="hljs-type">BufferedOutputStream</span> <span class="hljs-variable">bos</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">BufferedOutputStream</span>(<span class="hljs-keyword">new</span> <span class="hljs-title class_">FileOutputStream</span>(<span class="hljs-string">&quot;output.txt&quot;</span>))) &#123;<br>    <span class="hljs-type">byte</span>[] array = <span class="hljs-string">&quot;JavaGuide&quot;</span>.getBytes();<br>    bos.write(array);<br>&#125; <span class="hljs-keyword">catch</span> (IOException e) &#123;<br>    e.printStackTrace();<br>&#125;<br></code></pre></td></tr></table></figure>

<p><code>BufferedOutputStream</code> 内部也维护了一个缓冲区，大小也是 <strong>8192</strong> 字节。</p>
<h2 id="字符缓冲流"><a href="#字符缓冲流" class="headerlink" title="字符缓冲流"></a>字符缓冲流</h2><p><code>BufferedReader</code> （字符缓冲输入流）和 <code>BufferedWriter</code>（字符缓冲输出流）类似于 <code>BufferedInputStream</code>（字节缓冲输入流）和<code>BufferedOutputStream</code>（字节缓冲输入流），内部都维护了一个字节数组作为缓冲区。不过，前者主要是用来操作字符信息。</p>
<h2 id="打印流"><a href="#打印流" class="headerlink" title="打印流"></a>打印流</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs java">System.out.print(<span class="hljs-string">&quot;Hello！&quot;</span>);<br>System.out.println(<span class="hljs-string">&quot;Hello！&quot;</span>);<br></code></pre></td></tr></table></figure>

<p><code>System.out</code> 实际是用于获取一个 <code>PrintStream</code> 对象，<code>print</code>方法实际调用的是 <code>PrintStream</code> 对象的 <code>write</code> 方法。</p>
<p><code>PrintStream</code> 属于字节打印流，与之对应的是 <code>PrintWriter</code> （字符打印流）。<code>PrintStream</code> 是 <code>OutputStream</code> 的子类，<code>PrintWriter</code> 是 <code>Writer</code> 的子类。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">PrintStream</span> <span class="hljs-keyword">extends</span> <span class="hljs-title class_">FilterOutputStream</span><br>    <span class="hljs-keyword">implements</span> <span class="hljs-title class_">Appendable</span>, Closeable &#123;<br>&#125;<br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">PrintWriter</span> <span class="hljs-keyword">extends</span> <span class="hljs-title class_">Writer</span> &#123;<br>&#125;<br></code></pre></td></tr></table></figure>

<h2 id="随机访问流"><a href="#随机访问流" class="headerlink" title="随机访问流"></a>随机访问流</h2><p>这里要介绍的随机访问流指的是支持随意跳转到文件的任意位置进行读写的 <code>RandomAccessFile</code> 。</p>
<p><code>RandomAccessFile</code> 的构造方法如下，我们可以指定 <code>mode</code>（读写模式）。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-title function_">RandomAccessFile</span><span class="hljs-params">(File file, String mode)</span><br>    <span class="hljs-keyword">throws</span> FileNotFoundException &#123;<br>    <span class="hljs-built_in">this</span>(file, mode, <span class="hljs-literal">false</span>);<br>&#125;<br><span class="hljs-comment">// 私有方法</span><br><span class="hljs-comment">// openAndDelete 参数默认为 false 表示打开文件并且这个文件不会被删除</span><br><span class="hljs-keyword">private</span> <span class="hljs-title function_">RandomAccessFile</span><span class="hljs-params">(File file, String mode, <span class="hljs-type">boolean</span> openAndDelete)</span>  <span class="hljs-keyword">throws</span> FileNotFoundException&#123;<br>  <span class="hljs-comment">// 省略大部分代码</span><br>&#125;<br></code></pre></td></tr></table></figure>

<p>读写模式主要有下面四种：</p>
<ul>
<li><code>r</code> : 只读模式。</li>
<li><code>rw</code>: 读写模式</li>
<li><code>rwd</code> : 相对于 <code>rw</code>，<code>rwd</code> 同步更新对“文件的内容”的修改到外部存储设备。</li>
<li><code>rws</code>: 相对于 <code>rw</code>，<code>rws</code> 同步更新对“文件的内容”或“元数据”的修改到外部存储设备。</li>
</ul>
<p>文件内容指的是文件中实际保存的数据，元数据则是用来描述文件属性比如文件的大小信息、创建和修改时间。</p>
<p><code>RandomAccessFile</code> 中有一个文件指针用来表示下一个将要被写入或者读取的字节所处的位置。我们可以通过 <code>RandomAccessFile</code> 的 <code>seek(long pos)</code> 方法来设置文件指针的偏移量（距文件开头 <code>pos</code> 个字节处）。如果想要获取文件指针当前的位置的话，可以使用 <code>getFilePointer()</code> 方法。</p>
<p><code>RandomAccessFile</code> 代码示例：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-type">RandomAccessFile</span> <span class="hljs-variable">randomAccessFile</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">RandomAccessFile</span>(<span class="hljs-keyword">new</span> <span class="hljs-title class_">File</span>(<span class="hljs-string">&quot;input.txt&quot;</span>), <span class="hljs-string">&quot;rw&quot;</span>);<br>System.out.println(<span class="hljs-string">&quot;读取之前的偏移量：&quot;</span> + randomAccessFile.getFilePointer() + <span class="hljs-string">&quot;,当前读取到的字符&quot;</span> + (<span class="hljs-type">char</span>) randomAccessFile.read() + <span class="hljs-string">&quot;，读取之后的偏移量：&quot;</span> + randomAccessFile.getFilePointer());<br><span class="hljs-comment">// 指针当前偏移量为 6</span><br>randomAccessFile.seek(<span class="hljs-number">6</span>);<br>System.out.println(<span class="hljs-string">&quot;读取之前的偏移量：&quot;</span> + randomAccessFile.getFilePointer() + <span class="hljs-string">&quot;,当前读取到的字符&quot;</span> + (<span class="hljs-type">char</span>) randomAccessFile.read() + <span class="hljs-string">&quot;，读取之后的偏移量：&quot;</span> + randomAccessFile.getFilePointer());<br><span class="hljs-comment">// 从偏移量 7 的位置开始往后写入字节数据</span><br>randomAccessFile.write(<span class="hljs-keyword">new</span> <span class="hljs-title class_">byte</span>[]&#123;<span class="hljs-string">&#x27;H&#x27;</span>, <span class="hljs-string">&#x27;I&#x27;</span>, <span class="hljs-string">&#x27;J&#x27;</span>, <span class="hljs-string">&#x27;K&#x27;</span>&#125;);<br><span class="hljs-comment">// 指针当前偏移量为 0，回到起始位置</span><br>randomAccessFile.seek(<span class="hljs-number">0</span>);<br>System.out.println(<span class="hljs-string">&quot;读取之前的偏移量：&quot;</span> + randomAccessFile.getFilePointer() + <span class="hljs-string">&quot;,当前读取到的字符&quot;</span> + (<span class="hljs-type">char</span>) randomAccessFile.read() + <span class="hljs-string">&quot;，读取之后的偏移量：&quot;</span> + randomAccessFile.getFilePointer());<br></code></pre></td></tr></table></figure>

<p><code>input.txt</code> 文件内容：</p>
<p><img src="C:\Users\69425\AppData\Roaming\Typora\typora-user-images\image-20231016123203978.png" srcset="/img/loading.gif" lazyload alt="image-20231016123203978"></p>
<p>输出：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs plain">读取之前的偏移量：0,当前读取到的字符A，读取之后的偏移量：1<br>读取之前的偏移量：6,当前读取到的字符G，读取之后的偏移量：7<br>读取之前的偏移量：0,当前读取到的字符A，读取之后的偏移量：1<br></code></pre></td></tr></table></figure>

<p><code>input.txt</code> 文件内容变为 <code>ABCDEFGHIJK</code> 。</p>
<p><code>RandomAccessFile</code> 的 <code>write</code> 方法在写入对象的时候如果对应的位置已经有数据的话，会将其覆盖掉。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-type">RandomAccessFile</span> <span class="hljs-variable">randomAccessFile</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">RandomAccessFile</span>(<span class="hljs-keyword">new</span> <span class="hljs-title class_">File</span>(<span class="hljs-string">&quot;input.txt&quot;</span>), <span class="hljs-string">&quot;rw&quot;</span>);<br>randomAccessFile.write(<span class="hljs-keyword">new</span> <span class="hljs-title class_">byte</span>[]&#123;<span class="hljs-string">&#x27;H&#x27;</span>, <span class="hljs-string">&#x27;I&#x27;</span>, <span class="hljs-string">&#x27;J&#x27;</span>, <span class="hljs-string">&#x27;K&#x27;</span>&#125;);<br></code></pre></td></tr></table></figure>

<p>假设运行上面这段程序之前 <code>input.txt</code> 文件内容变为 <code>ABCD</code> ，运行之后则变为 <code>HIJK</code> 。</p>
<p><code>RandomAccessFile</code> 比较常见的一个应用就是实现大文件的 <strong>断点续传</strong> 。断点续传就是上传文件中途暂停或失败（比如遇到网络问题）之后，不需要重新上传，只需要上传那些未成功上传的文件分片即可。分片（先将文件切分成多个文件分片）上传是断点续传的基础。</p>
<p><code>RandomAccessFile</code> 可以帮助我们合并文件分片，示例代码如下：</p>
<p><img src="https://oss.javaguide.cn/github/javaguide/java/io/20210609164749122.png" srcset="/img/loading.gif" lazyload alt="img"></p>
<p><code>RandomAccessFile</code> 的实现依赖于 <code>FileDescriptor</code> (文件描述符) 和 <code>FileChannel</code> （内存映射文件）。</p>

                
              </div>
            
            <hr/>
            <div>
              <div class="post-metas my-3">
  
    <div class="post-meta mr-3 d-flex align-items-center">
      <i class="iconfont icon-category"></i>
      

<span class="category-chains">
  
  
    
      <span class="category-chain">
        
  <a href="/categories/IO/" class="category-chain-item">IO</a>
  
  

      </span>
    
  
</span>

    </div>
  
  
    <div class="post-meta">
      <i class="iconfont icon-tags"></i>
      
        <a href="/tags/IO/" class="print-no-link">#IO</a>
      
    </div>
  
</div>


              
  

  <div class="license-box my-3">
    <div class="license-title">
      <div>IO基础知识</div>
      <div>http://example.com/2021/04/06/IO/Java IO基础知识/</div>
    </div>
    <div class="license-meta">
      
        <div class="license-meta-item">
          <div>作者</div>
          <div>fyy</div>
        </div>
      
      
        <div class="license-meta-item license-meta-date">
          <div>发布于</div>
          <div>2021年4月6日</div>
        </div>
      
      
      
        <div class="license-meta-item">
          <div>许可协议</div>
          <div>
            
              
              
                <a class="print-no-link" target="_blank" href="https://creativecommons.org/licenses/by/4.0/">
                  <span class="hint--top hint--rounded" aria-label="BY - 署名">
                    <i class="iconfont icon-by"></i>
                  </span>
                </a>
              
            
          </div>
        </div>
      
    </div>
    <div class="license-icon iconfont"></div>
  </div>



              
                <div class="post-prevnext my-3">
                  <article class="post-prev col-6">
                    
                    
                      <a href="/2021/04/09/%E8%AF%BE%E7%A8%8B%E5%AE%9E%E9%AA%8C/Win10%E5%9C%A8VMware%E4%B8%AD%E5%AE%89%E8%A3%85Ubuntu18.04.5/" title="Win10在VMware中安装Ubuntu18.04.5">
                        <i class="iconfont icon-arrowleft"></i>
                        <span class="hidden-mobile">Win10在VMware中安装Ubuntu18.04.5</span>
                        <span class="visible-mobile">上一篇</span>
                      </a>
                    
                  </article>
                  <article class="post-next col-6">
                    
                    
                      <a href="/2021/04/06/IO/Java%20IO%E6%A8%A1%E5%9E%8B/" title="IO模型">
                        <span class="hidden-mobile">IO模型</span>
                        <span class="visible-mobile">下一篇</span>
                        <i class="iconfont icon-arrowright"></i>
                      </a>
                    
                  </article>
                </div>
              
            </div>

            
  
  
    <article id="comments" lazyload>
      
  <div id="valine"></div>
  <script type="text/javascript">
    Fluid.utils.loadComments('#valine', function() {
      Fluid.utils.createScript('https://lib.baomitu.com/valine/1.5.1/Valine.min.js', function() {
        var options = Object.assign(
          {"appId":"ufph8TbmK43d1JfQxLvss4KY-MdYXbMMI","appKey":"96APiPOtZEiCQlv5h3jjZfbC","path":"window.location.pathname","placeholder":null,"avatar":"retro","meta":["nick","mail","link"],"requiredFields":[],"pageSize":10,"lang":"zh-CN","highlight":false,"recordIP":false,"serverURLs":"","emojiCDN":null,"emojiMaps":null,"enableQQ":false},
          {
            el: "#valine",
            path: window.location.pathname
          }
        )
        new Valine(options);
        Fluid.utils.waitElementVisible('#valine .vcontent', () => {
          var imgSelector = '#valine .vcontent img:not(.vemoji)';
          Fluid.plugins.imageCaption(imgSelector);
          Fluid.plugins.fancyBox(imgSelector);
        })
      });
    });
  </script>
  <noscript>Please enable JavaScript to view the comments</noscript>


    </article>
  


          </article>
        </div>
      </div>
    </div>

    <div class="side-col d-none d-lg-block col-lg-2">
      
  <aside class="sidebar" style="margin-left: -1rem">
    <div id="toc">
  <p class="toc-header">
    <i class="iconfont icon-list"></i>
    <span>目录</span>
  </p>
  <div class="toc-body" id="toc-body"></div>
</div>



  </aside>


    </div>
  </div>
</div>





  



  



  



  



  







    

    
      <a id="scroll-top-button" aria-label="TOP" href="#" role="button">
        <i class="iconfont icon-arrowup" aria-hidden="true"></i>
      </a>
    

    
      <div class="modal fade" id="modalSearch" tabindex="-1" role="dialog" aria-labelledby="ModalLabel"
     aria-hidden="true">
  <div class="modal-dialog modal-dialog-scrollable modal-lg" role="document">
    <div class="modal-content">
      <div class="modal-header text-center">
        <h4 class="modal-title w-100 font-weight-bold">搜索</h4>
        <button type="button" id="local-search-close" class="close" data-dismiss="modal" aria-label="Close">
          <span aria-hidden="true">&times;</span>
        </button>
      </div>
      <div class="modal-body mx-3">
        <div class="md-form mb-5">
          <input type="text" id="local-search-input" class="form-control validate">
          <label data-error="x" data-success="v" for="local-search-input">关键词</label>
        </div>
        <div class="list-group" id="local-search-result"></div>
      </div>
    </div>
  </div>
</div>

    

    
  </main>

  <footer>
    <div class="footer-inner">
  
    <div class="footer-content">
       <a href="https://hexo.io" target="_blank" rel="nofollow noopener"><span>Hexo</span></a> <i class="iconfont icon-love"></i> <a href="https://github.com/fluid-dev/hexo-theme-fluid" target="_blank" rel="nofollow noopener"><span>Fluid</span></a> 
    </div>
  
  
    <div class="statistics">
  
  

  
    
      <span id="leancloud-site-pv-container" style="display: none">
        总访问量
        <span id="leancloud-site-pv"></span>
        次
      </span>
    
    
      <span id="leancloud-site-uv-container" style="display: none">
        总访客数
        <span id="leancloud-site-uv"></span>
        人
      </span>
    
    

  
</div>

  
  
  
</div>

  </footer>

  <!-- Scripts -->
  
  <script  src="https://lib.baomitu.com/nprogress/0.2.0/nprogress.min.js" ></script>
  <link  rel="stylesheet" href="https://lib.baomitu.com/nprogress/0.2.0/nprogress.min.css" />

  <script>
    NProgress.configure({"showSpinner":false,"trickleSpeed":100})
    NProgress.start()
    window.addEventListener('load', function() {
      NProgress.done();
    })
  </script>


<script  src="https://lib.baomitu.com/jquery/3.6.4/jquery.min.js" ></script>
<script  src="https://lib.baomitu.com/twitter-bootstrap/4.6.1/js/bootstrap.min.js" ></script>
<script  src="/js/events.js" ></script>
<script  src="/js/plugins.js" ></script>


  <script  src="https://lib.baomitu.com/typed.js/2.0.12/typed.min.js" ></script>
  <script>
    (function (window, document) {
      var typing = Fluid.plugins.typing;
      var subtitle = document.getElementById('subtitle');
      if (!subtitle || !typing) {
        return;
      }
      var text = subtitle.getAttribute('data-typed-text');
      
        typing(text);
      
    })(window, document);
  </script>




  
    <script  src="/js/img-lazyload.js" ></script>
  




  
<script>
  Fluid.utils.createScript('https://lib.baomitu.com/tocbot/4.20.1/tocbot.min.js', function() {
    var toc = jQuery('#toc');
    if (toc.length === 0 || !window.tocbot) { return; }
    var boardCtn = jQuery('#board-ctn');
    var boardTop = boardCtn.offset().top;

    window.tocbot.init(Object.assign({
      tocSelector     : '#toc-body',
      contentSelector : '.markdown-body',
      linkClass       : 'tocbot-link',
      activeLinkClass : 'tocbot-active-link',
      listClass       : 'tocbot-list',
      isCollapsedClass: 'tocbot-is-collapsed',
      collapsibleClass: 'tocbot-is-collapsible',
      scrollSmooth    : true,
      includeTitleTags: true,
      headingsOffset  : -boardTop,
    }, CONFIG.toc));
    if (toc.find('.toc-list-item').length > 0) {
      toc.css('visibility', 'visible');
    }

    Fluid.events.registerRefreshCallback(function() {
      if ('tocbot' in window) {
        tocbot.refresh();
        var toc = jQuery('#toc');
        if (toc.length === 0 || !tocbot) {
          return;
        }
        if (toc.find('.toc-list-item').length > 0) {
          toc.css('visibility', 'visible');
        }
      }
    });
  });
</script>


  <script src=https://lib.baomitu.com/clipboard.js/2.0.11/clipboard.min.js></script>

  <script>Fluid.plugins.codeWidget();</script>


  
<script>
  Fluid.utils.createScript('https://lib.baomitu.com/anchor-js/4.3.1/anchor.min.js', function() {
    window.anchors.options = {
      placement: CONFIG.anchorjs.placement,
      visible  : CONFIG.anchorjs.visible
    };
    if (CONFIG.anchorjs.icon) {
      window.anchors.options.icon = CONFIG.anchorjs.icon;
    }
    var el = (CONFIG.anchorjs.element || 'h1,h2,h3,h4,h5,h6').split(',');
    var res = [];
    for (var item of el) {
      res.push('.markdown-body > ' + item.trim());
    }
    if (CONFIG.anchorjs.placement === 'left') {
      window.anchors.options.class = 'anchorjs-link-left';
    }
    window.anchors.add(res.join(', '));

    Fluid.events.registerRefreshCallback(function() {
      if ('anchors' in window) {
        anchors.removeAll();
        var el = (CONFIG.anchorjs.element || 'h1,h2,h3,h4,h5,h6').split(',');
        var res = [];
        for (var item of el) {
          res.push('.markdown-body > ' + item.trim());
        }
        if (CONFIG.anchorjs.placement === 'left') {
          anchors.options.class = 'anchorjs-link-left';
        }
        anchors.add(res.join(', '));
      }
    });
  });
</script>


  
<script>
  Fluid.utils.createScript('https://lib.baomitu.com/fancybox/3.5.7/jquery.fancybox.min.js', function() {
    Fluid.plugins.fancyBox();
  });
</script>


  <script>Fluid.plugins.imageCaption();</script>

  <script defer src="/js/leancloud.js" ></script>

  <script  src="/js/local-search.js" ></script>





<!-- 主题的启动项，将它保持在最底部 -->
<!-- the boot of the theme, keep it at the bottom -->
<script  src="/js/boot.js" ></script>


  

  <noscript>
    <div class="noscript-warning">博客在允许 JavaScript 运行的环境下浏览效果更佳</div>
  </noscript>
</body>
</html>
